/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.rafkind.raytracer;

import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JProgressBar;

/**
 *
 * @author drafkind
 */
public class Scene {
  private Camera camera;
  private List<Light> lights;
  private List<VisibleObject> visibleObjects;

  public Scene() {
    lights = new ArrayList<Light>();
    visibleObjects = new ArrayList<VisibleObject>();
  }

  private Vector3 blinnPhongShade(Ray eyeRay, Vector3 intersectionPoint, Vector3 normal, Texture texture) {
    // for each light, shoot another ray at it from the intersectionpoint
    // to see if it is visible
    Set<Light> availableLights = new HashSet<Light>();
    for (Light ell : lights) {
      Ray towardsLight = Ray.createPositionally(intersectionPoint, ell.getPosition());
      towardsLight.translateAlong(Util.EPSILON);
      List<IntersectionPoint> iPoints = 
              towardsLight
              .shootAt(visibleObjects);
      if (iPoints.isEmpty() ||
              intersectionPoint.getDistanceTo(ell.getPosition()) <
              iPoints.get(0).getDistanceFromRayOrigin()) {
        availableLights.add(ell);
      }
    }

    Vector3 answer = texture.getPigmentAt(intersectionPoint).times(texture.getAmbient());
    for (Light ell : availableLights) {
      Vector3 towardsLight = Ray.createPositionally(intersectionPoint, ell.getPosition()).getDirection();
      Vector3 diffuse = ell.getColorAt(intersectionPoint).times(texture.getDiffuse()).times(towardsLight.dot(normal));

      Vector3 reverseEye = eyeRay.getDirection().times(-1);
      Vector3 h = (reverseEye.plus(towardsLight)).getNormalized();
      Vector3 specular = ell.getColorAt(intersectionPoint).times(texture.getSpecular()).times(Math.pow(h.dot(normal), texture.getShinyness()));
      answer = answer.plus(diffuse).plus(specular);
    }

    return answer.getClamped(new Vector3(1, 1, 1));
  }

  private void shoot(Ray ray) {
    
    List<IntersectionPoint> iPoints = ray.shootAt(visibleObjects);
    
    if (!iPoints.isEmpty()) {
      
      // look at the first one
      IntersectionPoint first = iPoints.get(0);

      Texture firstTexture = first.getTarget().getTexture();
      
      ray.setColor(blinnPhongShade(ray,
              first.getPoint(),
              first.getTarget().getNormalAt(first),
              firstTexture));
    }
  }

  public BufferedImage render(JProgressBar progressBar) {
    BufferedImage image = new BufferedImage(
            camera.getImageWidth(),
            camera.getImageHeight(), 
            BufferedImage.TYPE_INT_RGB);

    for (int j = 0; j < camera.getImageHeight(); j++) {
      for (int i = 0; i < camera.getImageWidth(); i++) {
        Ray ray = camera.createRay(i, j);

        shoot(ray);

        image.setRGB(i, j, ray.getColor().getRGB());
      }
      progressBar.setValue(100 * j / (camera.getImageHeight() -1));
    }
    return image;
  }

  public Camera getCamera() { return camera; }
  public void setCamera(Camera c) { camera = c; }
  
  public List<Light> getLights() { return lights; }
  public List<VisibleObject> getObjects() { return visibleObjects; }
}